package se.his.assignment1.server;

/**
 * Created by IntelliJ IDEA.
 * User: djurczak
 * Date: Jan 23, 2009
 * Time: 1:24:14 PM
 * To change this template use File | Settings | File Templates.
 */

import java.net.*;
import java.util.Vector;
import java.util.Iterator;
import java.io.IOException;

class ClientConnection {
    private final String m_name;
    private final InetAddress m_address;
    private final int m_port;

    public ClientConnection(String name, InetAddress address, int port) {
        m_name = name;
        m_address = address;
        m_port = port;
    }

    public void sendMessage(String message, DatagramSocket socket) {

        byte[] buf =  message.getBytes();
        DatagramPacket packet = new DatagramPacket(buf, buf.length, m_address, m_port);
        
        try {
            socket.send(packet);
        } catch (IOException e) {
            e.printStackTrace();  //To change body of catch statement use File | Settings | File Templates.
        }
    }

    public boolean hasName(String testName) {
        return testName.equals(m_name);
    }
}

public class Server {

    private Vector<ClientConnection> m_connectedClients = new Vector<ClientConnection>();
    private DatagramSocket m_socket;

    public static void main(String[] args) {
        if (args.length < 1) {
            System.err.println("Usage: java Server portnumber");
            System.exit(-1);
        }
        try {
            Server instance = new Server(Integer.parseInt(args[0]));
            instance.listenForClientMessages();
        } catch (NumberFormatException e) {
            System.err.println("Error: port number must be an integer.");
            System.exit(-1);
        }
    }

    private Server(int portNumber) {
        try {
            m_socket = new DatagramSocket(portNumber);
        } catch (SocketException e) {
            e.printStackTrace();  //To change body of catch statement use File | Settings | File Templates.
        }
    }

    private void listenForClientMessages() {
        boolean run = true;

        System.out.println("Waiting for client messages... ");

        do {
            String msgType = "empty";

            // TODO: Listen for client messages.
            // On reception of message, do the following:
            // * Unmarshal message

            // * Depending on message type, either
            //    - Try to create a new ClientConnection using addClient(), send
            //      response message to client detailing whether it was successful
            //    - Broadcast the message to all connected users using broadcast()
            //    - Send a private message to a user using sendPrivateMessage()

            byte[] buf = new byte[256];
            DatagramPacket packet = new DatagramPacket(buf, buf.length);

            try {
                m_socket.receive(packet);
            } catch (IOException e) {
                e.printStackTrace();  //To change body of catch statement use File | Settings | File Templates.
            }

           
            String from = "empty";
            String to = "empty";
            String message = "empty";

       //     CheckPacket(packet, from, to, message, msgType);

                   String received = new String(packet.getData(), 0, packet.getLength());

        String[] contents = received.split(";");

        msgType = contents[0];
        from = contents[1];
        if(contents.length>2)
            message = contents[2];

            if(msgType.equals("ConnectionRequest")){
                if(addClient(from, packet.getAddress(), packet.getPort())==true){
                    acceptConn(from);
                }
                else {
                    refuseConn(from, packet.getAddress(), packet.getPort(), "");

                }
            }
            else if(msgType.equals("ChatMessage")){
                broadcast(from + ";" + message);
            }
            else if(msgType.equals("PrivateMsg")){
                sendPrivateMessage(from + ";" + message + ";private", to);
            }
            else {
                // shouldn't happen !
            }
                                                                                                                    
        } while (run);

        m_socket.close();
    }

    private void refuseConn(String from, InetAddress address, int port, String message) {
        byte[] buf =  message.getBytes();
        DatagramPacket packet = new DatagramPacket(buf, buf.length, address, port);
        try {
            m_socket.send(packet);
        } catch (IOException e) {
            e.printStackTrace();  //To change body of catch statement use File | Settings | File Templates.
        }        
        
    }

    private void acceptConn(String name) {
         sendPrivateMessage("success", name);
    }

    public boolean addClient(String name, InetAddress address, int port) {
        ClientConnection c;
        for (Iterator<ClientConnection> itr = m_connectedClients.iterator(); itr.hasNext();) {
            c = itr.next();
            if (c.hasName(name)) {
                return false; // Already exists a client with this name
            }
        }
        m_connectedClients.add(new ClientConnection(name, address, port));
        return true;
    }

    public void sendPrivateMessage(String message, String name) {
        ClientConnection c;
        for (Iterator<ClientConnection> itr = m_connectedClients.iterator(); itr.hasNext();) {
            c = itr.next();
            if (c.hasName(name)) {
                c.sendMessage(message, m_socket);
            }
        }
    }

    public void broadcast(String message) {
        for (Iterator<ClientConnection> itr = m_connectedClients.iterator(); itr.hasNext();) {
            itr.next().sendMessage(message, m_socket);
        }
    }
}
